﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.Xml;
using System.Xml.Linq;
using System;
using c2iGMailNotifier.Messaging;
using c2iGMailNotifier.Model;
using c2iGMailNotifier.Service;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using System.Collections.ObjectModel;

namespace c2iGMailNotifier.ViewModel
{
    public class EmailsListViewModel : ViewModelBase
    {

        /// <summary>
        /// Initializes a new instance of the EmailsListViewModel class.
        /// </summary>
        public EmailsListViewModel()
        {
            this.AtomEntries = new ObservableCollection<AtomEntry>();

            if (IsInDesignMode)
            {
                this.AtomEntries.Add(
                    new AtomEntry
                        {
                            AuthorEMail = "rc@c2i.fr",
                            AuthorName = "Richard Clark",
                            Id = Guid.NewGuid().ToString(),
                            Issued = DateTime.Now,
                            Modified = DateTime.Now,
                            Link = "http://www.c2i.fr",
                            Summary = "summary of the email",
                            Title = "email from Richard Clark"
                        });
                this.AtomEntries.Add(
                    new AtomEntry
                        {
                            AuthorEMail = "toto@c2i.fr",
                            AuthorName = "Toto Clark",
                            Id = Guid.NewGuid().ToString(),
                            Issued = DateTime.Now.AddHours(-1),
                            Modified = DateTime.Now.AddHours(-1),
                            Link = "http://www.microsoft.fr",
                            Summary = "summary of the email 2",
                            Title = "email from toto Clark"
                        });
                this.AtomEntries.Add(
                    new AtomEntry
                        {
                            AuthorEMail = "titi@c2i.fr",
                            AuthorName = "Titi Clark",
                            Id = Guid.NewGuid().ToString(),
                            Issued = DateTime.Now.AddDays(-1),
                            Modified = DateTime.Now.AddDays(-1),
                            Link = "http://www.google.fr",
                            Summary = "summary of the email 3",
                            Title = "A long text for the title of this email"
                        });
            }
            else
            {
                CreateRefreshCommand();
                CreateNavigateCommand();

                int refreshInterval = 5;
                User user = SettingsReaderWriter.GetCurrentUser();
                if (user != null)
                    refreshInterval = user.RefreshInterval;

                timer = new DispatcherTimer(DispatcherPriority.Normal) {Interval = new TimeSpan(0, refreshInterval, 0)};
                timer.Tick += delegate { ExecuteRefreshCommand(); };
                timer.Start();

                backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
                backgroundWorker.RunWorkerCompleted +=
                    new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
                //backgroundWorker.RunWorkerAsync();

                // listening to RefreshMessage
                Messenger.Default.Register(this, delegate(RefreshMessage message) { ExecuteRefreshCommand(); });

                Messenger.Default.Register(this, delegate(UserSettingsChanged message)
                                                     {
                                                         User currentUser =
                                                             Service.SettingsReaderWriter.GetCurrentUser();
                                                         if (currentUser != null)
                                                         {
                                                             timer.Interval = new TimeSpan(0,
                                                                                           currentUser.RefreshInterval,
                                                                                           0);
                                                             ExecuteRefreshCommand();
                                                         }
                                                     });
            }
        }

        #region Fields
        private DispatcherTimer timer;
        private BackgroundWorker backgroundWorker;
        #endregion

        #region Properties

        #region IsWorking Property

        /// <summary>
        /// The <see cref="IsWorking" /> property's name.
        /// </summary>
        public const string IsWorkingPropertyName = "IsWorking";

        private bool _isWorking;

        public bool IsWorking
        {
            get { return this._isWorking; }

            private set
            {
                bool oldValue = _isWorking;
                if (this._isWorking == value) return;

                this._isWorking = value;
                RaisePropertyChanged(IsWorkingPropertyName, oldValue, value, true);
            }
        }

        #endregion

        #region AtomEntries Property

        /// <summary>
        /// The <see cref="AtomEntries" /> property's name.
        /// </summary>
        public const string AtomEntriesPropertyName = "AtomEntries";

        private ObservableCollection<AtomEntry> _atomEntries;

        public ObservableCollection<AtomEntry> AtomEntries
        {
            get { return this._atomEntries; }

            private set
            {
                if (this._atomEntries == value) return;

                this._atomEntries = value;
                RaisePropertyChanged(AtomEntriesPropertyName);
            }
        }
        #endregion

        #region HasError Property

        /// <summary>
        /// The <see cref="HasError" /> property's name.
        /// </summary>
        public const string HasErrorPropertyName = "HasError";

        private bool _hasError;
        public bool HasError
        {
            get { return this._hasError; }

            private set
            {
                if (this._hasError == value) return;

                this._hasError = value;
                RaisePropertyChanged(HasErrorPropertyName);
            }
        }

        #endregion

        #endregion 

        #region Commands
        #region RefreshCommand

        private void CreateRefreshCommand()
        {
            RefreshCommand = new RelayCommand(ExecuteRefreshCommand, CanExecuteRefreshCommand);
        }

        public ICommand RefreshCommand { get; internal set; }

        public void ExecuteRefreshCommand()
        {
            if (!CanExecuteRefreshCommand())
                return;

            // lancement de la recherche d'emails dans un autre thread 
            IsWorking = true;
            backgroundWorker.RunWorkerAsync();
        }

        public bool CanExecuteRefreshCommand()
        {
            User user = SettingsReaderWriter.GetCurrentUser();

            return
                !this.IsWorking &&
                !backgroundWorker.IsBusy;
        }

        #endregion

        #region NavigateCommand

        private void CreateNavigateCommand()
        {
            NavigateCommand = new RelayCommand<string>(ExecuteNavigateCommand);
        }

        public ICommand NavigateCommand { get; internal set; }


        public void ExecuteNavigateCommand(string argument)
        {
            System.Diagnostics.Process.Start(argument);
        }
        #endregion
        #endregion

        #region private Methods
        void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                HasError = true;
                this.AtomEntries.Clear();
            }
            else
            {
                HasError = false;
                List<AtomEntry> rResult = e.Result as List<AtomEntry>;
                if (rResult == null)
                    throw new ArgumentNullException("e", "Result must be a RefreshResult and not null");

                // If there is a new entry, play sound
                foreach (AtomEntry atomEntry in rResult)
                {
                    if (!this.AtomEntries.Contains(atomEntry))
                    {
                        MediaPlayer mplayer = new MediaPlayer();
                        mplayer.Open(new Uri("newEmail.wav", UriKind.Relative));
                        mplayer.Play();
                        break;
                    }
                }

                this.AtomEntries.Clear();
                foreach (AtomEntry entry in rResult)
                {
                    this.AtomEntries.Add(entry);
                }
            }
            IsWorking = false;
            Messenger.Default.Send(new RefreshDoneMessage(this) { LastUpdateDate = DateTime.Now });

        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            List<AtomEntry> result = new List<AtomEntry>();

            User user = SettingsReaderWriter.GetCurrentUser();

            StringBuilder sb = new StringBuilder(user.UserName);
            sb.Append(":");
            sb.Append(user.Password);
            Byte[] bytes = Encoding.ASCII.GetBytes(sb.ToString());

            HttpWebRequest feedRequest =
                (HttpWebRequest) WebRequest.Create("https://mail.google.com/mail/feed/atom");
            feedRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(bytes));

            HttpWebResponse response = (HttpWebResponse) feedRequest.GetResponse();
            Stream feedStream = response.GetResponseStream();

            XmlReader feedReader = XmlReader.Create(feedStream);

            XElement elt = XElement.Load(feedReader);
            XNamespace ns = elt.GetDefaultNamespace();
            IEnumerable<XElement> items = from item in elt.Elements(ns + "entry")
                                          select item;

            foreach (XElement item in items)
            {
                XElement author = item.Elements(ns + "author").FirstOrDefault();
                result.Add(new AtomEntry
                                           {
                                               Title = item.Elements(ns + "title").FirstOrDefault().Value,
                                               Summary = item.Elements(ns + "summary").FirstOrDefault().Value,
                                               Link =
                                                   item.Elements(ns + "link").FirstOrDefault().Attribute("href").Value,
                                               Modified =
                                                   ConvertDate(item.Elements(ns + "modified").FirstOrDefault().Value),
                                               Issued = ConvertDate(item.Elements(ns + "issued").FirstOrDefault().Value),
                                               Id = item.Elements(ns + "id").FirstOrDefault().Value,
                                               AuthorName = author.Elements(ns + "name").FirstOrDefault().Value,
                                               AuthorEMail = author.Elements(ns + "email").FirstOrDefault().Value
                                           });

            }


            e.Result = result;
        }

        private DateTime ConvertDate(string atomDate)
        {
            DateTime d;

            if (DateTime.TryParse(atomDate, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out d))
                return d;
            // Bogue GMail ? Il y a des dates commencant par 24:
            // sample: 2009-08-25T24:45:28Z -> 2009-08-26T00:45:28Z 
            int tPos = atomDate.IndexOf("T");
            if (tPos <= 0 || atomDate.Substring(tPos + 1, 3) != "24:")
                throw new ArgumentException("atom Date Format unknown1:" + atomDate, "atomDate");

            atomDate = atomDate.Substring(0, tPos) + "T23:" + atomDate.Substring(tPos + 4);
            //atomDate = 0
            if (DateTime.TryParse(atomDate, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out d))
                return d.AddHours(25);

            throw new ArgumentException("atom Date Format unknown2:" + atomDate, "atomDate");
        }
        #endregion
    }
}